#include "software_iic.h"

/* 微秒级延时函数 - 基于SysTick，适配168MHz系统时钟 */
void Delay_us(uint32_t udelay)
{
    uint32_t startval, tickn, delays, wait;
    
    startval = SysTick->VAL;
    tickn = HAL_GetTick();
    delays = udelay * 168; // 168MHz系统时钟
    
    if(delays > startval) {
        while(HAL_GetTick() == tickn) {
            // 等待tick变化
        }
        wait = 168000 + startval - delays;
        while(wait < SysTick->VAL) {
            // 精确延时
        }
    } else {
        wait = startval - delays;
        while(wait < SysTick->VAL && HAL_GetTick() == tickn) {
            // 精确延时
        }
    }
}

/* 基本时序操作 */
static void IIC_Delay(void)
{
    Delay_us(10); // I2C时序延时10微秒
}

/* 软件I2C基础函数 */
void IIC_Start(void)
{
    SDA_HIGH(); // SDA先置高
    SCL_HIGH(); // SCL置高
    IIC_Delay();
    SDA_LOW();  // SDA拉低产生起始信号
    IIC_Delay();
    SCL_LOW();  // SCL拉低
}

void IIC_Stop(void)
{
    SDA_LOW();  // SDA先置低
    IIC_Delay();
    SCL_HIGH(); // SCL置高
    IIC_Delay();
    SDA_HIGH(); // SDA置高产生停止信号
    IIC_Delay();
}

unsigned char IIC_WaitAck(void)
{
    unsigned char ack;
    SDA_HIGH(); // 释放SDA
    SCL_HIGH(); // SCL置高
    IIC_Delay();
    ack = READ_SDA(); // 读取应答信号
    SCL_LOW();  // SCL拉低
    IIC_Delay();
    return ack; // 返回应答状态
}

void IIC_SendAck(void)
{
    SDA_LOW();  // SDA拉低发送应答
    SCL_HIGH(); // SCL置高
    IIC_Delay();
    SCL_LOW();  // SCL拉低
    SDA_HIGH(); // 释放SDA
}

void IIC_SendNAck(void)
{
    SDA_HIGH(); // SDA置高发送非应答
    SCL_HIGH(); // SCL置高
    IIC_Delay();
    SCL_LOW();  // SCL拉低
}

unsigned char IIC_SendByte(unsigned char dat)
{
    for(unsigned char i = 0; i < 8; i++) {
        (dat & 0x80) ? SDA_HIGH() : SDA_LOW(); // 发送数据位
        dat <<= 1;
        SCL_HIGH(); // SCL置高
        IIC_Delay();
        SCL_LOW();  // SCL拉低
        IIC_Delay();
    }
    return IIC_WaitAck(); // 等待应答
}

unsigned char IIC_RecvByte(void)
{
    unsigned char dat = 0;
    SDA_HIGH(); // 释放SDA
    
    /* 接收数据前切换SDA为输入模式 */
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;  // 输入模式
    GPIO_InitStruct.Pull = GPIO_PULLUP;      // 上拉
    HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct);
    
    for(unsigned char i = 0; i < 8; i++) {
        dat <<= 1;
        SCL_HIGH(); // SCL置高
        IIC_Delay();
        if(READ_SDA()) dat |= 0x01; // 读取数据位
        SCL_LOW();  // SCL拉低
        IIC_Delay();
    }
    
    /* 恢复SDA为开漏输出模式 */
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
    HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct);
    
    return dat; // 返回接收的数据
}

/* 应用层接口函数 - 与hardware_iic.c保持完全一致 */
unsigned char IIC_ReadByte(unsigned char Salve_Address)
{
    unsigned char dat;

    IIC_Start();
    IIC_SendByte(Salve_Address | 0x01); // 读模式
    dat = IIC_RecvByte();
    IIC_SendNAck();
    IIC_Stop();

    return dat;
}

unsigned char IIC_ReadBytes(unsigned char Salve_Address, unsigned char Reg_Address,
                          unsigned char *Result, unsigned char len)
{
    IIC_Start();
    if(IIC_SendByte(Salve_Address & 0xFE)) { // 写模式
        IIC_Stop();
        return 0;
    }
    if(IIC_SendByte(Reg_Address)) {
        IIC_Stop();
        return 0;
    }
    IIC_Start(); // 重新起始
    if(IIC_SendByte(Salve_Address | 0x01)) { // 读模式
        IIC_Stop();
        return 0;
    }

    for(unsigned char i = 0; i < len; i++) {
        Result[i] = IIC_RecvByte();
        (i == len-1) ? IIC_SendNAck() : IIC_SendAck(); // 最后一字节发送NACK
    }
    IIC_Stop();
    return 1; // 成功返回1
}

unsigned char IIC_WriteByte(unsigned char Salve_Address, unsigned char Reg_Address,
                          unsigned char data)
{
    IIC_Start();
    if(IIC_SendByte(Salve_Address & 0xFE)) { // 写模式
        IIC_Stop();
        return 0;
    }
    if(IIC_SendByte(Reg_Address)) {
        IIC_Stop();
        return 0;
    }
    if(IIC_SendByte(data)) {
        IIC_Stop();
        return 0;
    }
    IIC_Stop();
    return 1; // 成功返回1
}

unsigned char IIC_WriteBytes(unsigned char Salve_Address, unsigned char Reg_Address,
                           unsigned char *data, unsigned char len)
{
    IIC_Start();
    if(IIC_SendByte(Salve_Address & 0xFE)) {
        IIC_Stop();
        return 0;
    }
    if(IIC_SendByte(Reg_Address)) {
        IIC_Stop();
        return 0;
    }

    for(unsigned char i = 0; i < len; i++) {
        if(IIC_SendByte(data[i])) {
            IIC_Stop();
            return 0;
        }
    }
    IIC_Stop();
    return 1; // 成功返回1
}

/* 灰度传感器专用函数 - 与hardware_iic.c保持完全一致 */
unsigned char Ping(void)
{
    unsigned char dat;
    IIC_ReadBytes(GW_GRAY_ADDR_DEF<<1, GW_GRAY_PING, &dat, 1);
    if(dat == GW_GRAY_PING_OK) {
        return 0; // 连接正常
    }
    else return 1; // 连接异常
}

unsigned char IIC_Get_Digtal(void)
{
    unsigned char dat = 0;
    if(1 == IIC_ReadBytes(GW_GRAY_ADDR_DEF<<1, GW_GRAY_DIGITAL_MODE, &dat, 1)) {
        return dat; // 返回数字量数据
    }
    return 0xAA; // 错误返回值
}

unsigned char IIC_Get_Anolog(unsigned char *Result, unsigned char len)
{
    if(IIC_ReadBytes(GW_GRAY_ADDR_DEF<<1, GW_GRAY_ANALOG_BASE_, Result, len)) return 1;
    else return 0;
}

unsigned char IIC_Get_Single_Anolog(unsigned char Channel)
{
    unsigned char dat;
    IIC_ReadBytes(GW_GRAY_ADDR_DEF<<1, GW_GRAY_ANALOG(Channel), &dat, 1);
    return dat;
}

unsigned char IIC_Anolog_Normalize(uint8_t Normalize_channel)
{
    return IIC_WriteBytes(GW_GRAY_ADDR_DEF<<1, 0xCF, &Normalize_channel, 1);
}

unsigned short IIC_Get_Offset(void)
{
    unsigned char dat[2] = {0};
    IIC_ReadBytes(GW_GRAY_ADDR_DEF<<1, Offset, dat, 2);
    return (unsigned short)dat[0] | (unsigned short)dat[1]<<8; // 组合16位数据
}
